package com.xcity.game.shop;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.xcity.db.entity.Player;
import com.xcity.game.common.Cause;
import com.xcity.game.common.Currency;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.game.item.ItemEffect;
import com.xcity.game.item.ItemException;
import com.xcity.game.item.ItemTemplate;
import com.xcity.game.shop.peak.PeakShopHandler;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.msg.BatchItemsPush;
import com.xcity.pomelo.request.GetShopItemsRequest;
import com.xcity.pomelo.request.ShopBuyRequest;
import com.xcity.pomelo.request.ShopBuyV2Request;
import com.xcity.pomelo.request.ShopFastBuyInfoRequest;
import com.xcity.pomelo.request.ShopRefreshV2Request;
import com.xcity.pomelo.request.ShopV2Request;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.Response;
import com.xcity.pomelo.response.ShopBuyResponse;
import com.xcity.pomelo.response.ShopBuyV2Response;
import com.xcity.pomelo.response.ShopFastBuyInfoResponse;
import com.xcity.pomelo.response.ShopItemListResponse;
import com.xcity.pomelo.response.ShopRefreshV2Response;
import com.xcity.pomelo.response.ShopV2Response;
import com.xcity.pomelo.response.ShopV2Response.ShopItemV2;
import com.xcity.pomelo.response.ShopV2Response.ShopV2;

import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import naga.x.App;
import naga.x.game.template.TemplateService;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;
import naga.x.util.NumberUtils;

@OPHandler
public class ShopService implements Service, MessageConst {

	protected List<ShopItemTemplate> goods; // 所有商品列表
	protected Map<ShopType, List<ShopItemTemplate>> goodsByGroup; // 按商店类型分组列表
//	protected Map<ShopType, TIntObjectMap<ShopItemTemplate>> goodsMapByGroup;
	protected TIntObjectMap<ShopItemTemplate> goodsMap; // key: itemId
	protected Map<String, ShopItemTemplate> goodsUniqueMap; // key: uniqueId
	protected TIntObjectMap<List<ShopItemTemplate>> fastBuyMap; // 快速购买用到的物品映射（同一个物品可能分不同价格卖/打包卖）
	
	private ShopHandler[] handlers;
	
	private class CommonShopHandler implements ShopHandler {
		private ShopType type;
		private CommonShopHandler(ShopType type) {
			this.type = type;
		}
		@Override
		public ShopV2 requestShop(Player player) {
			ShopV2 shop = new ShopV2();
			shop.type = type;
			List<ShopItemTemplate> list = goodsByGroup.get(type);
			if (type.visible && list != null && !list.isEmpty()) {
				shop.items = new ArrayList<ShopItemV2>(list.size());
				for (ShopItemTemplate template : list) {
					if (template.isVisible()) {
						int availableCount = ShopUtils.getAvailableCount(player, template);
						ShopItemV2 item = new ShopItemV2(template, template.getPriceType(), availableCount);
						shop.items.add(item);
					}
				}
			}
			return shop;
		}
		@Override
		public ShopBuyV2Response requestBuy(Session session, ShopBuyV2Request req) throws ShopException {
			Player player = session.getClient();
			try {
				ShopType reqType = req.getType() == type.ordinal() ? type : null;
				ShopItemTemplate item = doCommonBuy(player, reqType, req.getId(), req.getCount());
				int availableCount = ShopUtils.getAvailableCount(player, item);
				session.send(new BatchItemsPush(item.getItem(), req.getCount() * item.getSalesCount()));
				ShopBuyV2Response res = new ShopBuyV2Response(req.getSerial(), item.getGroup(), item.getUniqueId(), availableCount);
				return res;
			} catch (ShopException e) {
				throw e;
			}
		}
		@Override
		public ShopRefreshV2Response requestRefresh(Session session, ShopRefreshV2Request req) throws ShopException {
			throw new ShopException(MessageConst.ILLEGAL_OPERATION);
		}
		@Override
		public void unRegister() {
		}
	}
	
	@Override
	public void startup() throws ServiceException {
		goods = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(ShopItemTemplate.class);
		goodsByGroup = new EnumMap<ShopType, List<ShopItemTemplate>>(ShopType.class);
//		goodsMapByGroup = new EnumMap<ShopType, TIntObjectMap<ShopItemTemplate>>(ShopType.class);
		goodsUniqueMap = new HashMap<String, ShopItemTemplate>(goods.size());
		for (int i = 0; i < goods.size(); i++) {
			ShopItemTemplate item = goods.get(i);
			// map
//			TIntObjectMap<ShopItemTemplate> map = goodsMapByGroup.get(item.getGroup());
//			if (map == null) {
//				map = new TIntObjectHashMap<ShopItemTemplate>();
//				goodsMapByGroup.put(item.getGroup(), map);
//			}
//			map.put(item.getId(), item);
			// list
			List<ShopItemTemplate> list = goodsByGroup.get(item.getGroup());
			if (list == null) {
				list = new LinkedList<ShopItemTemplate>();
				goodsByGroup.put(item.getGroup(), list);
			}
			list.add(item);
			
			goodsUniqueMap.put(item.getUniqueId(), item);
		}
		goodsMap = App.getApp().getServiceManager().get(TemplateService.class).getTemplates(ShopItemTemplate.class);
		
		handlers = new ShopHandler[] {
				new CommonShopHandler(ShopType.EXCHANGE), // EXCHANGE
				new CommonShopHandler(ShopType.ITEM), // ITEM
				new CommonShopHandler(ShopType.PET), // PET
				new CommonShopHandler(ShopType.ARENA), // ARENA
				new PeakShopHandler(), // PEAK
				new CommonShopHandler(ShopType.GUILDSCORE) // GUILDSCORE
		};
	}

	@Override
	public void shutdown() throws ServiceException {
		for (ShopHandler handler : handlers) {
			handler.unRegister();
		}
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	@Deprecated
	@PomeloOP(PomeloRoute.SHOP_ITEM_LIST)
	protected void requestShopItems(Session session, GetShopItemsRequest req) {
		session.send(new ShopItemListResponse(req.getSerial(), session.getClient(), goods));
	}
	
	@Deprecated
	@PomeloOP(PomeloRoute.SHOP_BUY)
	protected void requestBuy(Session session, ShopBuyRequest req) {
		try {
			Player player = session.getClient();
			ShopItemTemplate item = doCommonBuy(player, req.getType(), req.getItemId(), req.getNum());
			session.send(new ShopBuyResponse(req.getSerial(), player, item));
			session.send(new BatchItemsPush(item.getItem(), req.getNum() * item.getSalesCount()));
		} catch (ShopException e) {
			session.send(new HintResponse(req.getSerial(), e.getMessage()));
		}
	}
	
	private ShopItemTemplate doCommonBuy(Player player, ShopType shopType, String uniqueId, int count) throws ShopException {
		if (count < 1) {
			throw new ShopException(ILLEGAL_OPERATION);
		}
		ShopItemTemplate template = goodsUniqueMap.get(uniqueId);
		if (template == null) {
			// 尝试当做itemId获取
			int itemId = NumberUtils.parseInt(uniqueId);
			if (itemId == 0 || (template = goodsMap.get(itemId)) == null) {
				throw new ShopException(SHOP_ITEM_NOT_EXIST, ShopException.TYPE_ITEM_NOT_FOUND);
			}
		}
		// 检测限购
		if (template.getNumLimit() > 0 && ShopUtils.getBoughtNumToday(player, template) >= template.getNumLimit()) {
			throw new ShopException(SHOP_ITEM_SOLD_OUT, ShopException.TYPE_ITEM_SOLD_OUT);
		}
		int availableCount = ShopUtils.getAvailableCount(player, template);
		if (availableCount != ShopItemV2.AVAILABLE_ENOUGH && availableCount < count) {
			throw new ShopException(MessageFormat.format(SHOP_BUY_COUNT_MAXED, availableCount, template.getName()));
		}
		if (player.getBag().isFull()) {
			throw new ShopException(BAG_FULL);
		}
		Currency currency = template.getPriceType();
		if (!player.cost(currency, template.getSalesPrice() * count, Cause.SHOP_BUY)) {
			throw new ShopException(MessageFormat.format(CURRENCY_NOT_ENOUGH, currency.name));
		}
		try {
			count = count * template.getSalesCount();
			player.getBag().addItem(template.getId(), count, Cause.SHOP_BUY);
			if (template.getNumLimit() > 0) {
				ShopUtils.addBoughtNum(player, template, count);
			}
			App.getApp().getEventManager().addEvent(GameEvents.EVENT_BUY_ITEM, player, template.getId());
			return template;
		} catch (ItemException e) {
			throw new ShopException(e.getMessage());
		}
	}
	
	ShopHandler getHandler(byte shopType) {
		return shopType >= 0 && shopType < handlers.length ? handlers[shopType] : null;
	}

	//---new op handler---
	@PomeloOP(PomeloRoute.SHOP_V2)
	protected void requestShop(Session session, ShopV2Request req) {
		ShopHandler handler = getHandler(req.getType());
		Player player = session.getClient();
		if (handler == null) { // request all
			List<ShopV2> shops = new ArrayList<ShopV2>(handlers.length);
			for (ShopHandler sh : handlers) {
				ShopV2 shop = sh.requestShop(player);
				if (shop != null && !shop.items.isEmpty()) {
					shops.add(shop);
				}
			}
			session.send(new ShopV2Response(req.getSerial(), shops));
			return;
		}
		ShopV2 shop = handler.requestShop(player);
		session.send(new ShopV2Response(req.getSerial(), Arrays.asList(shop)));
	}
	
	@PomeloOP(PomeloRoute.SHOP_BUY_V2)
	protected void requestBuy(Session session, ShopBuyV2Request req) {
		ShopHandler handler = getHandler(req.getType());
		if (handler == null) {
			String error = SHOP_ITEM_NOT_EXIST;
			for (ShopHandler sh : handlers) {
				try {
					ShopBuyV2Response res = sh.requestBuy(session, req);
					session.send(res);
					return;
				} catch (ShopException e) {
					error = e.getMessage();
					if (e.type != ShopException.TYPE_ITEM_NOT_FOUND) {
						break;
					}
				}
			}
			session.send(new HintResponse(req.getSerial(), error));
			return;
		}
		try {
			ShopBuyV2Response res = handler.requestBuy(session, req);
			session.send(res);
		} catch (ShopException e) {
			session.send(new HintResponse(req.getSerial(), e.getMessage()));
		}
	}
	
	@PomeloOP(PomeloRoute.SHOP_REFRESH_V2)
	protected void requestRefresh(Session session, ShopRefreshV2Request req) {
		ShopHandler handler = getHandler(req.getType());
		if (handler == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		try {
			Response res = handler.requestRefresh(session, req);
			session.send(res);
		} catch (ShopException e) {
			session.send(new HintResponse(req.getSerial(), e.getMessage()));
		}
	}
	
	@PomeloOP(PomeloRoute.FAST_BUY_INFO)
	protected void requestFastBuyInfo(Session session, ShopFastBuyInfoRequest req) {
		ItemTemplate item;
		if (req.getId() == 0
				|| (item = App.getApp().getServiceManager().get(TemplateService.class).get(ItemTemplate.class, req.getId())) == null
				/*|| goodsMap.get(req.getId()) == null*/) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ITEM_NOT_EXIST));
			return;
		}
		TIntObjectMap<List<ShopItemTemplate>> map = fastBuyMap;
		if (map == null) {
			fastBuyMap = map = new TIntObjectHashMap<List<ShopItemTemplate>>();
		}
		List<ShopItemTemplate> list = map.get(item.getId());
		if (list == null) {
			list = Collections.emptyList();
			for (int i = 0, size = goods.size(); i < size; i++) {
				ShopItemTemplate it = goods.get(i);
				if (it.getId() == item.getId()
						|| (it.getItemTemplate().getEffect() == ItemEffect.ADD_ITEM && it.getItemTemplate().getParameter(0) == item.getId())) {
					if (list.isEmpty()) {
						list = new LinkedList<ShopItemTemplate>();
					}
					list.add(it);
				}
			}
			map.put(item.getId(), list);
		}
		if (list.isEmpty()) {
			session.send(new ShopFastBuyInfoResponse(req.getSerial()));
			return;
		}
		Player player = session.getClient();
		List<ShopItemV2> resList = new ArrayList<ShopItemV2>(list.size());
		for (ShopItemTemplate it : list) {
			ShopItemV2 res = new ShopItemV2(it, it.getPriceType(), ShopUtils.getAvailableCount(player, it));
			resList.add(res);
		}
		session.send(new ShopFastBuyInfoResponse(req.getSerial(), resList));
	}
	//---new op handler---
	
}
